#include <iostream>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include "mensajes.h"
#include <Client.h>
#include <Logger.h>

#define SHELP "-h"
#define SINSERT 'i'
#define SREMOVE 'r'
#define SCONSULT 'c'
#define SUPDATE 'u'
#define SQUIT 'q'

void showHelp() {
	std::cout << std::endl;
	std::cout << "DBGestor Client" << std::endl;
	std::cout << "************************" << std::endl;
	std::cout << "Comandos : " << std::endl;
	std::cout << "-i [Nombre] [Dirección] [Teléfono] Inserta un registro."
			<< std::endl;
	std::cout << "-r [Nombre] Borra un registro." << std::endl;
	std::cout << "-c [Nombre] Muestra un registro" << std::endl;
	std::cout << "-u [Nombre] [Dirección] [Teléfono] Actualiza un registro"
			<< std::endl;
	std::cout << "-q Salir de la aplicación." << std::endl;
	std::cout << "************************" << std::endl;
	std::cout << std::endl;
}

persona_st parseInput(const std::string& operation, bool parseDirAndTel) {

	Logger* log = Logger::getInstance();

	std::string aux = operation;
	persona_st persona;
	memset(&persona, 0, sizeof(persona_st));

	unsigned int namePos = aux.find('[');
	unsigned int namePosEnd = aux.find(']');
	if ((namePos != std::string::npos) && (namePosEnd != std::string::npos)) {

		std::string name = aux.substr(namePos + 1, namePosEnd - namePos - 1);
		if (name.length() > sizeof(persona_st::nombre)) {
			log->log(ERROR, "[Client] Tamaño del nombre superior a 61 caracteres");
			throw("El tamaño del nombre debe ser menor a 61 caracteres.");
		}
		memcpy(persona.nombre, name.c_str(), name.length());
		log->log(INFO, "[Client] Nombre parseado: "+name);

	}

	else {
		log->log(ERROR, "[Client] Nombre no encontrado para la operacion solicitada");
		throw("Campo mal formado, por favor revisar.");
	}

	if (parseDirAndTel) {
		aux = aux.substr(namePosEnd + 1, std::string::npos);
		unsigned int dirPos = aux.find('[');
		unsigned int dirPosEnd = aux.find(']');
		if ((dirPos != std::string::npos) && (dirPosEnd != std::string::npos)) {
			std::string dir = aux.substr(dirPos + 1, dirPosEnd - dirPos - 1);
			if (dir.length() > sizeof(persona_st::direccion)) {
				log->log(ERROR, "[Client] Tamaño de la direccion superior a los 120 caracteres");
				throw("El tamaño de la direccion debe ser menor a 120 caracteres.");
			}
			memcpy(persona.direccion, dir.c_str(), dir.length());
			log->log(INFO, "[Client] Direccion parseada : "+dir);
		} else {
			log->log(ERROR, "[Client] Direccion no encontrada para la operacion solicitada");
			throw("Campo mal formado, por favor revisar.");
		}

		aux = aux.substr(dirPosEnd + 1, std::string::npos);
		unsigned int telPos = aux.find('[');
		unsigned int telPosEnd = aux.find(']');
		if ((telPos != std::string::npos) && (telPosEnd != std::string::npos)) {
			std::string tel = aux.substr(telPos + 1, telPosEnd - telPos - 1);
			if (tel.length() > sizeof(persona_st::telefono)) {
				log->log(ERROR, "[Client] Tamaño del telefono superior a los 13 caracteres");
				throw("El tamaño de la direccion debe ser menor a 13 caracteres.");
			}
			memcpy(persona.telefono, tel.c_str(), tel.length());
			log->log(INFO, "[Client] Telefono parseado : "+tel);
		} else {
			log->log(ERROR, "[Client] Telefono no encontrado para la operacion solicitada");
			throw("Campo mal formado, por favor revisar.");
		}

	}
	log->log(INFO, "[Client] Parseo de la los campos ingresados por el usuario realizado");
	return persona;

}

void processOperation(const std::string operation, bool* end) {
	Logger* log = Logger::getInstance();
	log->log(INFO, "[Client] Parseo operacion solicitada por el cliente");

	if (operation.length() < 2) {
		log->log(ERROR, "[Client] Comando ingresado invalido");
		throw("Comando inválido, por favor revise lo ingresado.");
	}

	if (operation.at(0) != '-') {
		log->log(ERROR, "[Client] Comando ingresado invalido");
		throw("Comando inválido, por favor revise lo ingresado.");
	}
	else {
		char opChar = operation.at(1);

		switch (opChar) {
		case SINSERT: {
			log->log(INFO, "[Client] Operacion insert parseada");
			persona_st person = parseInput(operation, true);
			Client client;
			client.sendRequest(ADD, person);
			break;
		}
		case SREMOVE: {
			log->log(INFO, "[Client] Operacion remove parseada");
			persona_st person = parseInput(operation, false);
			Client client;
			client.sendRequest(REMOVE, person);
			break;
		}
		case SCONSULT: {
			log->log(INFO, "[Client] Operacion consulta parseada");
			persona_st person = parseInput(operation, false);
			Client client;
			client.sendRequest(CONSULT, person);
			break;
		}
		case SUPDATE: {
			log->log(INFO, "[Client] Operacion update parseada");
			persona_st person = parseInput(operation, true);
			Client client;
			client.sendRequest(UPDATE, person);
			break;
		}
		case SQUIT: {
			log->log(INFO, "[Client] Operacion salir parseada");
			*end = true;
			break;
		}

		default: {
			log->log(ERROR, "[Client] Comando ingresado invalido");
			throw("Comando inválido, por favor revise lo ingresado.");
		}
		}
	}
}

int main() {
	Logger* log = Logger::getInstance();
	log->enabled(true);
	log->log(INFO, "[Client] Cliente iniciado");
	bool end = false;
	while (!end) {
		log->log(INFO, "[Client] Muestro comandos posibles");
		showHelp();

		std::cout << "Ingrese la operacion que desea realizar " << std::endl;
		std::string operation;

		std::getline(std::cin, operation);
		try {
			processOperation(operation, &end);
		} catch (const char* message) {
			std::cout << message << std::endl;
			std::cout << "Presione ENTER para continuar" << std::endl;
			std::cin.ignore();
		}

	}
	log->log(INFO, "[Client] Cliente terminado");
	return 0;
}

